/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.tabbedproperties.composites;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.text.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertyConstants;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
import org.eclipse.uml2.uml.ExtensionPoint;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.commands.AddUMLElementToDiagram;
import com.cea.papyrus.core.commands.MoveInListCommand;
import com.cea.papyrus.core.commands.UMLElementCreateCommand;
import com.cea.papyrus.core.commands.factories.DiagramElementDeleteCommandFactory;
import com.cea.papyrus.core.commands.factories.UMLDi2ElementDeleteFactory;
import com.cea.papyrus.core.editor.CommandStackUtils;
import com.cea.papyrus.core.editpart.GraphNodeEditPart;
import com.cea.papyrus.core.editpart.IUMLElementEditPart;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagram.usecase.editpart.ExtensionPointEditPart;
import com.cea.papyrus.diagram.usecase.editpart.ExtensionPointListEditPart;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.umlutils.Element;
import com.cea.papyrus.umlutils.UseCase;

/**
 * Displays the extension locations of an extend in a composite.
 * 
 * @author Gilles Cannenterre
 * @since 16 nov. 06
 */
public class ExtensionPointsComposite extends DecoratedTableComposite implements ISectionComposite {

	/**
	 * The Class EditItemListener.
	 */
	private class EditItemListener implements Listener {
		
		/**
		 * 
		 * 
		 * @param event 
		 */
		public void handleEvent(Event event) {
			TableItem item = table.getSelection()[0];
			editItem(item);
		}
	}
	
	/**
	 * The Class ViewElementButtonListener.
	 */
	private class ViewElementButtonListener implements MouseListener {
		
		/**
		 * Mouse double click.
		 * 
		 * @param e the e
		 */
		public void mouseDoubleClick(MouseEvent e) {
			// do nothing
		}
		
		/**
		 * Mouse down.
		 * 
		 * @param e the e
		 */
		public void mouseDown(MouseEvent e) {
			// do nothing
		}
		
		/**
		 * Mouse up.
		 * 
		 * @param e the e
		 */
		public void mouseUp(MouseEvent e) {
			TableItem[] tableItems = table.getSelection();
			viewElementButtonPressed(tableItems);
			refresh();
		}
	}

	/**
	 * The Constant IMG_DISPLAY.
	 */
	private static final Image IMG_DISPLAY	= PapyrusPlugin.getImage(PapyrusPlugin.PAPYRUS_ICONS_16x16+"ConsoleView_16x16.gif");

	/**
	 * The Constant IMG_DISPLAYED_STEREO.
	 */
	private static final Image IMG_DISPLAYED_EXTENSION_POINT	= PapyrusPlugin.getImage(PapyrusPlugin.PAPYRUS_ICONS_16x16+"DisplayedExtensionPoint_16x16.gif");

	/**
	 * The Constant IMG_EXTENSION_POINT.
	 */
	private static final Image IMG_EXTENSION_POINT	= PapyrusPlugin.getImage(PapyrusPlugin.UML_ICONS_16x16+"ExtensionPoint.gif");
	
	/**
	 * The view element button.
	 */
	protected Button viewElementButton;
	
	/**
	 * The view element button listener.
	 */
	protected MouseListener viewElementButtonListener;
	
	/**
	 * Default Constructor.
	 */
	public ExtensionPointsComposite() {
		super("Extension Points");
		this.viewElementButtonListener = new ViewElementButtonListener();
	}

	/**
	 * The Constant KEY.
	 */
	private final static String KEY = "extensionpoint_data";
	
	/**
	 * The util use case.
	 */
	private UseCase utilUseCase;
	
	/**
	 * The graphical element use case.
	 */
	private IUMLElementEditPart graphicalElementUseCase;

	/**
	 * Sets the element.
	 * 
	 * @param element the element
	 */
	@Override
	public void setElement(Element element) {
		super.setElement(element);
		this.utilUseCase = (UseCase)element;
	}

	/**
	 * Refresh.
	 */
	@Override
	public void refresh() {
		if( !table.isDisposed() && (utilUseCase!=null) && !viewElementButton.isDisposed()){			
			// Allow selection on the viewElement button if selection is a graphNode
			viewElementButton.setEnabled((graphicalElementUseCase instanceof GraphNodeEditPart));	
		
			// Remove parameters from table 
			table.removeAll();

			// Read and add parameter
			Iterator<ExtensionPoint> it = utilUseCase.getUml2UseCase().getExtensionPoints().iterator();
			while (it.hasNext()) {
				ExtensionPoint extensionpoint = it.next();
				TableItem item = new TableItem(table, SWT.BORDER);
				setItemImage(item,extensionpoint);
				item.setData(KEY, extensionpoint);
				item.setText(extensionpoint.getName());
			}
		}
	}

	/**
	 * Sets the item image.
	 * 
	 * @param item the item
	 * @param extensionPoint the extension point
	 */
	private void setItemImage(TableItem item,ExtensionPoint extensionPoint) {
		// Set the extension point image
		item.setImage(IMG_EXTENSION_POINT);
		
		if (graphicalElementUseCase instanceof GraphNodeEditPart){
			// Get editpart children
			for (int i = 0; i < ((GraphNodeEditPart)graphicalElementUseCase).getChildren().size(); i++){
				EditPart child = (EditPart)((GraphNodeEditPart)graphicalElementUseCase).getChildren().get(i);
				// Get the extension point list graphnode and set the displayed image if the
				// Extension point is displayed
				if (child instanceof ExtensionPointListEditPart){
					if (isExtensionPointDisplayed(extensionPoint, child)) {
						item.setImage(IMG_DISPLAYED_EXTENSION_POINT);
					}
				}
			}
		}
		
	}
	
	/**
	 * Adds the button pressed.
	 */
	@Override
	public void addButtonPressed() {
		UMLElementCreateCommand umlElementCreateCommand = new UMLElementCreateCommand(ExtensionPoint.class,utilUseCase.getUml2Element());
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(umlElementCreateCommand);
	}

	/**
	 * Down button pressed.
	 */
	@Override
	public void downButtonPressed() {
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();

		for(int i = (tableItems.length-1); i >= 0; i--) {
			// get extension point list
			List extensionPoints = utilUseCase.getUml2UseCase().getExtensionPoints();
			// get extension point
			ExtensionPoint extensionPoint = (ExtensionPoint) tableItems[i].getData(KEY);
			int newIndex = table.indexOf(tableItems[i]) + 1;
			if(newIndex < extensionPoints.size()) {
				//new MoveInListCommand(parameters, parameter, newIndex).execute();
				compoundCommand.add(new MoveInListCommand(extensionPoints, extensionPoint, newIndex));
			}
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(compoundCommand);
	}

	/**
	 * Edits the item.
	 * 
	 * @param item the item
	 */
	@Override
	public void editItem(TableItem item) {
		if (item == null) {
			return;
		}
		// parameter linked to the item
		ExtensionPoint extensionPoint = (ExtensionPoint)item.getData(KEY);
		
		// TODO edit an extension point
	}

	/**
	 * Removes the button pressed.
	 */
	@Override
	public void removeButtonPressed() {
		CompoundCommand command = new CompoundCommand(); 
		// get selection and delete it
		TableItem[] tableItems = table.getSelection();
		for(int i = 0; i < tableItems.length ; i++) {
			// get extension point
			ExtensionPoint extensionPoint = (ExtensionPoint) tableItems[i].getData(KEY);
			command.add(UMLDi2ElementDeleteFactory.eINSTANCE.createCommand(extensionPoint));
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(command);
	}

	/**
	 * Up button pressed.
	 */
	@Override
	public void upButtonPressed() {
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();
		// get extension point list
		List extensionPoints = utilUseCase.getUml2UseCase().getExtensionPoints();
		// get extension point
		for(int i = 0; i < tableItems.length ; i++) {
			// get extension point
			ExtensionPoint extensionPoint = (ExtensionPoint) tableItems[i].getData(KEY);
			int newIndex = table.indexOf(tableItems[i]) - 1;
			if (newIndex >= 0) {
				compoundCommand.add(new MoveInListCommand(extensionPoints, extensionPoint, newIndex));
			}
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(compoundCommand);
	}

	/**
	 * View element button pressed.
	 * 
	 * @param tableItems the table items
	 */
	public void viewElementButtonPressed(TableItem[] tableItems) {
		long timeStamp = System.nanoTime();
		Debug.debug(this, "viewElementButtonPressed", 300);
		if (graphicalElementUseCase instanceof GraphNodeEditPart){
			
			GraphNodeEditPart graphNodeEditPart = ((GraphNodeEditPart) graphicalElementUseCase);

			for(int i = 0 ; i < tableItems.length ; i++){
				// Get the extension point from selection
				ExtensionPoint extensionPoint = (ExtensionPoint)tableItems[i].getData(KEY);
						
				// Get editpart children
				for (int j = 0; j < graphNodeEditPart.getChildren().size(); j++) {
					EditPart child = (EditPart)graphNodeEditPart.getChildren().get(j);									
					// Get the extension point list graphnode
					if (child instanceof ExtensionPointListEditPart){
						// Get extension point list graphnode
						GraphNode extensionPointListGraphNode = ((ExtensionPointListEditPart)child).getGraphNode();
						
						// Create command
						CompoundCommand command = new CompoundCommand();
						
						// If extension point is already displayed
						if (isExtensionPointDisplayed(extensionPoint, child)) {
							// Remove extension point graphnode
							Iterator graphElements = lookForExtensionPointGraphNodes(extensionPoint, child).iterator();
							while(graphElements.hasNext()) {
								GraphElement ge = (GraphElement) graphElements.next();
								command.add(DiagramElementDeleteCommandFactory.eINSTANCE.createCommand(ge));
							}	
						} else {
							//	Add extension point graphnode
							command.add(new AddUMLElementToDiagram(extensionPoint,extensionPointListGraphNode,new Point(), new Dimension()));
						}
		
						// Execute command and refresh usecase editpart
						CommandStack stack = CommandStackUtils.getCommandStack();
						Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");								
						stack.execute(command);
									
						refresh();
					}												
				}			
			}

		}
		Debug.debug(this, "viewElementButtonPressed executed in: "+ (System.nanoTime()-timeStamp)/1000+" �s" , 300);
	}

	/**
	 * Look for extension point graph nodes.
	 * 
	 * @param child the extension point list edit part
	 * @param extensionPoint the extension point
	 * 
	 * @return the list
	 */
	private List lookForExtensionPointGraphNodes(ExtensionPoint extensionPoint, EditPart child) {
		List<GraphElement> listGraphElements = new ArrayList<GraphElement>();
		// Get the extension points
		for (int i = 0; i < child.getChildren().size(); i++) {
			EditPart editPart = (EditPart)child.getChildren().get(i);
			if (editPart instanceof ExtensionPointEditPart){
				if (((ExtensionPointEditPart)editPart).getUmlElement() == extensionPoint){				
					listGraphElements.add(((ExtensionPointEditPart)editPart).getGraphNode());
				}
			}
		}		
		return (List) listGraphElements;
	}

	/**
	 * Checks if is extension point is displayed.
	 * 
	 * @param extensionPointListEditPart the child
	 * @param extensionPoint the extension point
	 * 
	 * @return true, if is extension point is displayed
	 */
	private boolean isExtensionPointDisplayed(ExtensionPoint extensionPoint, EditPart extensionPointListEditPart) {
		boolean displayed = false;
		// Get the extension points
		Iterator it2 = extensionPointListEditPart.getChildren().iterator();
		while (it2.hasNext() && !displayed){
			EditPart editPart = (EditPart)it2.next();
			if (editPart instanceof ExtensionPointEditPart){
				if (((ExtensionPointEditPart)editPart).getUmlElement() == extensionPoint){				
					displayed = true;
				}
			}
		}
		return displayed;
	}

	/**
	 * Creates the content.
	 * 
	 * @param parent the parent
	 * @param factory the factory
	 * 
	 * @return the composite
	 */
	@Override
	public Composite createContent(Composite parent,
			TabbedPropertySheetWidgetFactory factory) {
		composite = factory.createFlatFormComposite(parent);
	
		FormData data;
	
		/////////////////////////////////////////////////////////////////////////////
		// Create and place button vertically on the left side
		// Button : Add Element
		// Button Delete Element
		removeButton = factory.createButton(composite, "",  SWT.PUSH);
		removeButton.setVisible(true);
		removeButton.setImage(DELETE_ELEMENT_IMG);
		removeButton.setToolTipText("Delete selected element(s)");
		data = new FormData();
		// data.top   = new FormAttachment(addButton, ITabbedPropertyConstants.HSPACE);
		data.top  = new FormAttachment(0, 0);
		data.right = new FormAttachment(100, -ITabbedPropertyConstants.HSPACE);
		removeButton.setLayoutData(data);
		removeButton.addMouseListener(removeButtonlistener);
		
		addButton = factory.createButton(composite, "", SWT.PUSH);
		addButton.setVisible(true);
		addButton.setImage(NEW_ELEMENT_IMG);
		addButton.setToolTipText("Add a new element");
		
		data = new FormData();
		// data.top   = new FormAttachment(label, ITabbedPropertyConstants.HSPACE);
		data.top  = new FormAttachment(0, 0);
		data.right = new FormAttachment(removeButton, -ITabbedPropertyConstants.HSPACE);
		addButton.setLayoutData(data);
		addButton.addMouseListener(addButtonlistener);
	
		// Button Up
		upButton = factory.createButton(composite, "", SWT.PUSH);
		upButton.setVisible(true);
		upButton.setImage(UP_ELEMENT_IMG);
		upButton.setToolTipText("Up");
	
		data = new FormData();
		// data.top   = new FormAttachment(removeButton, ITabbedPropertyConstants.HSPACE);
		data.top  = new FormAttachment(0, 0);
		data.right = new FormAttachment(addButton, -ITabbedPropertyConstants.HSPACE);
		upButton.setLayoutData(data);
		upButton.addMouseListener(upButtonlistener);
		
		
		// Button Down
		downButton = factory.createButton(composite, "", SWT.PUSH);
		downButton.setVisible(true);
		downButton.setImage(DOWN_ELEMENT_IMG);
		downButton.setToolTipText("Down");
	
		data = new FormData();
		// data.top   = new FormAttachment(upButton, ITabbedPropertyConstants.HSPACE);
		data.top  = new FormAttachment(0, 0);
		data.right = new FormAttachment(upButton, -ITabbedPropertyConstants.HSPACE);
		downButton.setLayoutData(data);
		downButton.addMouseListener(downButtonlistener);
		
		// Button ViewElement
		viewElementButton = factory.createButton(composite, "", SWT.PUSH);
		viewElementButton.setVisible(true);
		viewElementButton.setImage(IMG_DISPLAY);
		viewElementButton.setToolTipText("View Element");
	
		data = new FormData();
		// data.top   = new FormAttachment(upButton, ITabbedPropertyConstants.HSPACE);
		data.top  = new FormAttachment(0, 0);
		data.right = new FormAttachment(downButton, -ITabbedPropertyConstants.HSPACE);
		viewElementButton.setLayoutData(data);
		viewElementButton.addMouseListener(viewElementButtonListener);
		
		// Create label
		label = factory.createCLabel(composite, name+":", SWT.BORDER);
		label.setLayout(new FormLayout());
		data = new FormData();
		data.left = new FormAttachment(0, ITabbedPropertyConstants.HSPACE);
		data.right = new FormAttachment(viewElementButton, -ITabbedPropertyConstants.HSPACE);
		data.top  = new FormAttachment(0, 0);
		label.setLayoutData(data);
		
		/////////////////////////////////////////////////////////////////////////////
		// Create and place Table
		table = factory.createTable(composite, SWT.MULTI | SWT.H_SCROLL | SWT.BORDER);
		table.setLayout(new FormLayout());
		table.setVisible(true);
		table.addListener(SWT.MouseDoubleClick, tableListener = new EditItemListener());
	
		data = new FormData();
		data.height = 100;
		data.top    = new FormAttachment(label, ITabbedPropertyConstants.VSPACE);
		data.left   = new FormAttachment(0 , ITabbedPropertyConstants.HSPACE);
		data.right  = new FormAttachment(100, -ITabbedPropertyConstants.HSPACE);
		// data.bottom = new FormAttachment(100, ITabbedPropertyConstants.VSPACE);
	
		table.setLayoutData(data);
		return composite;
	}

	/**
	 * Sets the graphical element.
	 * 
	 * @param graphicalElement the graphical element
	 */
	public void setGraphicalElement(IUMLElementEditPart graphicalElement) {
		graphicalElementUseCase = graphicalElement;	
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#dispose()
	 */
	/**
	 * 
	 */
	public void dispose(){
		super.dispose();
		if(viewElementButton!=null && !viewElementButton.isDisposed())
			viewElementButton.removeMouseListener(viewElementButtonListener);
	}
}